package rw.ac.kiosc.jdbc.school;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactoryConfigurationError;
import org.w3c.dom.DOMException;
import rw.ac.kiosc.jdbc.school.dao.DepartmentDAO;
import rw.ac.kiosc.jdbc.school.dao.StudentDAO;
import rw.ac.kiosc.jdbc.school.orm.Department;
import rw.ac.kiosc.jdbc.school.orm.Student;

import javax.xml.parsers.*;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import rw.ac.kiosc.jdbc.school.dao.UserDAO;
import rw.ac.kiosc.jdbc.school.orm.User;
import java.security.MessageDigest;
import java.util.HashSet;
import rw.ac.kiosc.jdbc.school.hibernate.dao.DepartmentHibernateDAOImpl;
import rw.ac.kiosc.jdbc.school.hibernate.dao.StudentHibernateDAOImpl;
import rw.ac.kiosc.jdbc.school.hibernate.dao.UserHibernateDAOImpl;

public class School {

    public static final String KIOSC_PASSWORD_CIPHER = "HGreJ54gnb faj*&^%#Knmrbqfew342dgdg 6nav";

    public static ByteArrayOutputStream getSchoolByteArrayStream(School school) throws DOMException, TransformerFactoryConfigurationError, TransformerException, TransformerConfigurationException, IllegalArgumentException, ParserConfigurationException {
        DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = fact.newDocumentBuilder();
        Document document = builder.newDocument();
        Element root = document.createElement("school");
        for (Department department : school.getDepartmentDAO().getAllDepartments()) {
            //System.out.println("Department: " + department.getAcronym() + " - " + department.getName() + "\n-----------------------------------------------------");
            //int i = 0;
            //for (Student student : department.getStudents()) {
            //    System.out.println("    " + (++i) + ". " + student.getRegistrationNumber() + " - " + student.getName());
            //}
            //System.out.println("\n");
            root.appendChild(department.getDepartmentNode(document));
            //school.closeConnection();
        }
        document.appendChild(root);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        DOMSource source = new DOMSource(document);
        StreamResult result = new StreamResult(stream);
        TransformerFactory factory = TransformerFactory.newInstance();
        factory.setAttribute("indent-number", new Integer(4));
        Transformer transformer = factory.newTransformer();
        transformer.setOutputProperty(OutputKeys.METHOD, "xml");
        transformer.setOutputProperty(OutputKeys.ENCODING, "ISO-8859-1");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setParameter("indent-number", new Integer(4));
        transformer.transform(source, result);
        return stream;
    }
    private String dr = "com.mysql.jdbc.Driver";
    private String url = "jdbc:mysql://localhost:3306/";
    private String db = "school";
    private String u = "kiosc";
    private String p = "kiosc";
    private DepartmentDAO departmentDAO;
    private StudentDAO studentDAO;
    private UserDAO userDAO;
    private Connection connection;

    public School() {
        try {
            Class.forName(dr);
            connection = DriverManager.getConnection(url + db, u, p);
            //setDepartmentDAO(DepartmentDAOImpl.getInstance(connection));
            setDepartmentDAO(DepartmentHibernateDAOImpl.getInstance());
            //setStudentDAO(StudentDAOImpl.getInstance(connection));
            setStudentDAO(StudentHibernateDAOImpl.getInstance());
            //setUserDAO(UserDAOImpl.getInstance(connection));
            setUserDAO(UserHibernateDAOImpl.getInstance());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public Connection getConnection() {
        try {
            if (connection.isClosed()) {
                connection = DriverManager.getConnection(url + db, u, p);
                //setDepartmentDAO(DepartmentDAOImpl.getInstance(connection));
                setDepartmentDAO(DepartmentHibernateDAOImpl.getInstance());
                //setStudentDAO(StudentDAOImpl.getInstance(connection));
                setStudentDAO(StudentHibernateDAOImpl.getInstance());
                //setUserDAO(UserDAOImpl.getInstance(connection));
                setUserDAO(UserHibernateDAOImpl.getInstance());
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return connection;
    }

    private void setConnection(Connection connection) {
        this.connection = connection;
    }

    private void closeConnection() {
        try {
            connection.close();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
    }

    public DepartmentDAO getDepartmentDAO() {
        return departmentDAO;
    }

    public void setDepartmentDAO(DepartmentDAO departmentDAO) {
        this.departmentDAO = departmentDAO;
    }

    public StudentDAO getStudentDAO() {
        return studentDAO;
    }

    public void setStudentDAO(StudentDAO studentDAO) {
        this.studentDAO = studentDAO;
    }

    public UserDAO getUserDAO() {
        return userDAO;
    }

    public void setUserDAO(UserDAO userDAO) {
        this.userDAO = userDAO;
    }

    public List<Student> enroll(List<Student> students, Department department) {
        List<Student> returnList = new ArrayList<Student>();
        for (Student student : students) {
            returnList.add(enroll(student, department));
        }
        return returnList;
    }

    public Student enroll(Student student, Department department) {
        student.setDepartment(department);
        return getStudentDAO().saveOrUpdateStudent(student);
    }

    public User addUser(User user) {
        return this.getUserDAO().saveOrUpdateUser(user);
    }

    public User addUser(String username, String password, String name) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            md.reset();
            md.update((KIOSC_PASSWORD_CIPHER + password).getBytes());

            String hashedPassword = bytesToHexString(md.digest());
            return this.addUser(new User(username, hashedPassword, name));
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }

    public static String bytesToHexString(byte[] b) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < b.length; i++) {
            if ((b[i] & 0xff) < 0x10) {
                sb.append("0");
            }
            sb.append(Long.toString(b[i] & 0xff, 16));
        }
        return sb.toString();
    }

    public User authenticateUser(String username, String password) {
        User usr = this.getUserDAO().getUserByUsername(username);
        if (usr == null) {
            return null;
        } else {
            try {
                MessageDigest md = MessageDigest.getInstance("SHA-1");
                md.reset();
                md.update((KIOSC_PASSWORD_CIPHER + password).getBytes());
                String hashedPassword = bytesToHexString(md.digest());
                if (hashedPassword.equals(usr.getHashedPassword())) {
                    return usr;
                } else {
                    return null;
                }
            } catch (Exception ex) {
                ex.printStackTrace();
                return null;
            }
        }
    }

    public static void main(String args[]) {
        School school = new School();
        Department ceit = school.getDepartmentDAO().saveDepartment(new Department("CEIT", 5, "Computer Engineering and Information Technology", new HashSet<Student>()));
        Department ceet = school.getDepartmentDAO().saveDepartment(new Department("CEET", 4, "Civil Engineering and Environmental Technology", new HashSet<Student>()));
        Department eee = school.getDepartmentDAO().saveDepartment(new Department("EEE", 4, "Electrical and Electronics Engineering", new HashSet<Student>()));
        Department mee = school.getDepartmentDAO().saveDepartment(new Department("MEE", 5, "Mechanical Engineering", new HashSet<Student>()));
        school.enroll(new Student("GS20080461", "AYIRWANDA Jean Claude", 3, null), ceet);
        school.enroll(new Student("GS20030467", "MASABO Intore ya Sentore", 5, null), mee);
        school.enroll(new Student("GS20002651", "MUBUTU Sese Seko", 2, null), ceit);
        school.enroll(new Student("GS28030463", "HIRWA Steven", 1, null), ceet);
        school.enroll(new Student("GS22345465", "MBANZIRIZAMISHINGA Bernadette", 4, null), eee);
        school.addUser("kiosc", "kist", "KIST KIOSC, KIST Open Source Club");
        User user = school.authenticateUser("kiosc", "kist");// school.addUser("kiosc", "kist", "KIST KIOSC");
        if (user != null) {
            System.out.println(user.getUsername() + "/" + user.getHashedPassword() + " (" + user.getName() + ") authenticated successifully");
        } else {
            System.out.println("User not found");
        }
        try {
            ByteArrayOutputStream stream = getSchoolByteArrayStream(school);

            System.out.print(stream.toString());
            FileOutputStream fout = new FileOutputStream("/home/antoine/Desktop/school.xml");
            fout.write(stream.toByteArray());
            fout.flush();
            fout.close();
            stream.close();

        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }
}
